home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / pine / imap-3.0 / ANSI / c-client / os_mac.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-07-27  |  19.1 KB  |  676 lines

  1. /*
  2.  * Program:    Operating-system dependent routines -- Macintosh version
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        6158 Lariat Loop NE
  6.  *        Bainbridge Island, WA  98110-2098
  7.  *        Internet: MRC@Panda.COM
  8.  *
  9.  * Date:    26 January 1992
  10.  * Last Edited:    27 July 1993
  11.  *
  12.  * Copyright 1993 by Mark Crispin
  13.  *
  14.  *  Permission to use, copy, modify, and distribute this software and its
  15.  * documentation for any purpose and without fee is hereby granted, provided
  16.  * that the above copyright notice appears in all copies and that both the
  17.  * above copyright notices and this permission notice appear in supporting
  18.  * documentation, and that the name of Mark Crispin not be used in advertising
  19.  * or publicity pertaining to distribution of the software without specific,
  20.  * written prior permission.  This software is made available "as is", and
  21.  * MARK CRISPIN DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD TO
  22.  * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED WARRANTIES OF
  23.  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN NO EVENT SHALL
  24.  * MARK CRISPIN BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES
  25.  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  26.  * WHETHER IN AN ACTION OF CONTRACT, TORT (INCLUDING NEGLIGENCE) OR STRICT
  27.  * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
  28.  * THIS SOFTWARE.
  29.  *
  30.  */
  31.  
  32.  
  33. /*  This is a totally new operating-system dependent module for the Macintosh,
  34.  * written using THINK C on my Mac PowerBook-100 in my free time.
  35.  * Unlike earlier efforts, this version requires no external TCP library.  It
  36.  * also takes advantage of the Map panel in System 7 for the timezone.
  37.  */
  38.  
  39. #define BUFLEN (size_t) 8192    /* TCP input buffer */
  40.  
  41.  
  42. #include <limits.h>
  43. #include <time.h>
  44. #include <stdio.h>
  45. #include <MacTCPCommonTypes.h>
  46. #include <AddressXlation.h>
  47. #include <TCPPB.h>
  48. #include <Script.h>
  49.  
  50. /* TCP I/O stream (must be before osdep.h is included) */
  51.  
  52. #define TCPSTREAM struct tcp_stream
  53. TCPSTREAM {
  54.   char *host;            /* host name */
  55.   char *localhost;        /* local host name */
  56.   struct TCPiopb pb;        /* MacTCP parameter block */
  57.   long ictr;            /* input counter */
  58.   char *iptr;            /* input pointer */
  59.   char ibuf[BUFLEN];        /* input buffer */
  60. };
  61.  
  62. #include "mail.h"
  63. #include "osdep.h"
  64. #include "misc.h"
  65.  
  66. short TCPdriver = 0;        /* MacTCP's reference number */
  67. short resolveropen = 0;        /* TCP's resolver open */
  68.  
  69. /* Write current time in RFC 822 format
  70.  * Accepts: destination string
  71.  *
  72.  * This depends upon the ReadLocation() call in System 7 and the
  73.  * user properly setting his location/timezone in the Map control
  74.  * panel.
  75.  * Nothing is done about the gmtFlags.dlsDelta byte yet, since I
  76.  * don't know how it's supposed to work.
  77.  */
  78.  
  79. void rfc822_date (char *string)
  80. {
  81.   long tz,tzm;
  82.   time_t ti = time (0);
  83.   struct tm *t = localtime (&ti);
  84.   MachineLocation loc;
  85.   ReadLocation (&loc);        /* get location/timezone poop */
  86.   tz = loc.gmtFlags.gmtDelta |    /* get sign-extended time zone */
  87.     ((loc.gmtFlags.gmtDelta & 0x00800000) ? 0xff000000 : 0);
  88.   tz /= 60;            /* get timezone in minutes */
  89.   tzm = tz % 60;        /* get minutes from the hour */
  90.                 /* output time */
  91.   strftime (string,MAILTMPLEN,"%a, %d %b %Y %H:%M:%S ",t);
  92.                 /* now output time zone */
  93.   sprintf (string += strlen (string),"%+03ld%02ld",
  94.        tz/60,tzm >= 0 ? tzm : -tzm);
  95. }
  96.  
  97. /* Get a block of free storage
  98.  * Accepts: size of desired block
  99.  * Returns: free storage block
  100.  */
  101.  
  102. void *fs_get (size_t size)
  103. {
  104.   void *block = malloc (size);
  105.   if (!block) fatal ("Out of free storage");
  106.   return (block);
  107. }
  108.  
  109.  
  110. /* Resize a block of free storage
  111.  * Accepts: ** pointer to current block
  112.  *        new size
  113.  */
  114.  
  115. void fs_resize (void **block,size_t size)
  116. {
  117.   if (!(*block = realloc (*block,size))) fatal ("Can't resize free storage");
  118. }
  119.  
  120.  
  121. /* Return a block of free storage
  122.  * Accepts: ** pointer to free storage block
  123.  */
  124.  
  125. void fs_give (void **block)
  126. {
  127.   free (*block);
  128.   *block = NIL;
  129. }
  130.  
  131.  
  132. /* Report a fatal error
  133.  * Accepts: string to output
  134.  */
  135.  
  136. void fatal (char *string)
  137. {
  138.   mm_fatal (string);        /* pass up the string */
  139.                 /* nuke the resolver */
  140.   if (resolveropen) CloseResolver ();
  141.   abort ();            /* die horribly */
  142. }
  143.  
  144. /* Copy string with CRLF newlines
  145.  * Accepts: destination string
  146.  *        pointer to size of destination string
  147.  *        source string
  148.  *        length of source string
  149.  */
  150.  
  151. char *strcrlfcpy (char **dst,unsigned long *dstl,char *src,unsigned long srcl)
  152. {
  153.   long i,j;
  154.   char *d = src;
  155.                 /* count number of LF's in source string(s) */
  156.   for (i = srcl,j = 0; j < srcl; j++) if (*d++ == '\012') i++;
  157.   if (i > *dstl) {        /* resize if not enough space */
  158.     fs_give ((void **) dst);    /* fs_resize does an unnecessary copy */
  159.     *dst = (char *) fs_get ((*dstl = i) + 1);
  160.   }
  161.   d = *dst;            /* destination string */
  162.   while (srcl--) {        /* copy strings */
  163.     *d++ = *src++;        /* copy character */
  164.                 /* append line feed to bare CR */
  165.     if ((*src == '\015') && (src[1] != '\012')) *d++ = '\012';
  166.   }
  167.   *d = '\0';            /* tie off destination */
  168.   return *dst;            /* return destination */
  169. }
  170.  
  171.  
  172. /* Length of string after strcrlfcpy applied
  173.  * Accepts: source string
  174.  *        length of source string
  175.  */
  176.  
  177. unsigned long strcrlflen (STRING *s)
  178. {
  179.   unsigned long pos = GETPOS (s);
  180.   unsigned long i = SIZE (s);
  181.   unsigned long j = i;
  182.   while (j--) if ((SNX (s) == '\015') && ((CHR (s) != '\012') || !j)) i++;
  183.   SETPOS (s,pos);        /* restore old position */
  184.   return i;
  185. }
  186.  
  187. /* TCP/IP open
  188.  * Accepts: host name
  189.  *        contact port number
  190.  * Returns: TCP stream if success else NIL
  191.  */
  192.  
  193. TCPSTREAM *tcp_open (char *host,long port)
  194. {
  195.   TCPSTREAM *stream;
  196.   struct hostInfo hst;
  197.   struct TCPCreatePB *createpb;
  198.   struct TCPOpenPB *openpb;
  199.   char *s;
  200.   unsigned long i,j,k,l;
  201.   char tmp[MAILTMPLEN];
  202.                 /* init MacTCP */
  203.   if (!TCPdriver && OpenDriver ("\p.IPP",&TCPdriver))
  204.     fatal ("Can't init MacTCP");
  205.                 /* domain literal? */
  206.   if (host[0] == '[' && host[strlen (host)-1] == ']') {
  207.     if (((i = strtol (s = host+1,&s,10)) <= 255) && *s++ == '.' &&
  208.     ((j = strtol (s,&s,10)) <= 255) && *s++ == '.' &&
  209.     ((k = strtol (s,&s,10)) <= 255) && *s++ == '.' &&
  210.     ((l = strtol (s,&s,10)) <= 255) && *s++ == ']' && !*s) {
  211.       hst.addr[0] = (i << 24) + (j << 16) + (k << 8) + l;
  212.       hst.addr[1] = 0;        /* only one address to try! */
  213.       sprintf (hst.cname,"[%ld.%ld.%ld.%ld]",i,j,k,l);
  214.     }
  215.     else {
  216.       sprintf (tmp,"Bad format domain-literal: %.80s",host);
  217.       mm_log (tmp,ERROR);
  218.       return NIL;
  219.     }
  220.   }
  221.  
  222.   else {            /* look up host name */
  223.     if (!resolveropen && OpenResolver (NIL))
  224.       fatal ("Can't init domain resolver");
  225.     resolveropen = T;        /* note resolver open now */
  226.     if (StrToAddr (host,&hst,tcp_dns_result,NIL)) {
  227.       while (hst.rtnCode == cacheFault && wait ());
  228.                 /* kludge around MacTCP bug */
  229.       if (hst.rtnCode == outOfMemory) {
  230.     mm_log ("Re-initializing domain resolver",WARN);
  231.     CloseResolver ();    /* bop it on the head and try again */
  232.     OpenResolver (NIL);    /* note this will leak 12K */
  233.     StrToAddr (host,&hst,tcp_dns_result,NIL);
  234.     while (hst.rtnCode == cacheFault && wait ());
  235.       }
  236.       if (hst.rtnCode) {    /* still have error status? */
  237.     switch (hst.rtnCode) {    /* analyze return */
  238.     case nameSyntaxErr:
  239.       s = "Syntax error in name";
  240.       break;
  241.     case noResultProc:
  242.       s = "No result procedure";
  243.       break;
  244.     case noNameServer:
  245.       s = "No name server found";
  246.       break;
  247.     case authNameErr:
  248.       s = "Host does not exist";
  249.       break;
  250.     case noAnsErr:
  251.       s = "No name servers responding";
  252.       break;
  253.     case dnrErr:
  254.       s = "Name server returned an error";
  255.       break;
  256.     case outOfMemory:
  257.       s = "Not enough memory to resolve name";
  258.       break;
  259.     case notOpenErr:
  260.       s = "Driver not open";
  261.       break;
  262.     default:
  263.       s = NIL;
  264.       break;
  265.     }
  266.     if (s) sprintf (tmp,"%s: %.80s",s,host);
  267.     else sprintf (tmp,"Unknown resolver error (%ld): %.80s",
  268.               hst.rtnCode,host);
  269.     mm_log (tmp,ERROR);
  270.     return NIL;
  271.       }
  272.     }
  273.   }
  274.  
  275.                 /* create local TCP/IP stream */
  276.   stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM));
  277.   stream->ictr = 0;        /* initialize input */
  278.   stream->pb.ioCRefNum = TCPdriver;
  279.   createpb = &stream->pb.csParam.create;
  280.   openpb = &stream->pb.csParam.open;
  281.   stream->pb.csCode = TCPCreate;/* create a TCP stream */
  282.                 /* set up buffer for TCP */
  283.   createpb->rcvBuffLen = 4*BUFLEN;
  284.   createpb->rcvBuff = fs_get (createpb->rcvBuffLen);
  285.   createpb->notifyProc = NIL;    /* no special notify procedure */
  286.   createpb->userDataPtr = NIL;
  287.   if (PBControlSync (&stream->pb)) fatal ("Can't create TCP stream");
  288.                   /* open TCP connection */
  289.   stream->pb.csCode = TCPActiveOpen;
  290.   openpb->ulpTimeoutValue = 30;    /* time out after 30 seconds */
  291.   openpb->ulpTimeoutAction = T;
  292.   openpb->validityFlags = timeoutValue|timeoutAction;
  293.                 /* remote host (should try all) */
  294.   openpb->remoteHost = hst.addr[0];
  295.   openpb->remotePort = port;    /* caller specified remote port */
  296.   openpb->localPort = 0;    /* generate a local port */
  297.   openpb->tosFlags = 0;        /* no special TOS */
  298.   openpb->precedence = 0;    /* no special precedence */
  299.   openpb->dontFrag = 0;        /* allow fragmentation */
  300.   openpb->timeToLive = 255;    /* standards say 60, UNIX uses 255 */
  301.   openpb->security = 0;        /* no special security */
  302.   openpb->optionCnt = 0;    /* no IP options */
  303.   openpb->options[0] = 0;
  304.   openpb->userDataPtr = NIL;    /* no special data pointer */
  305.   PBControlAsync (&stream->pb);    /* now open the connection */
  306.   while (stream->pb.ioResult == inProgress && wait ());
  307.   if (stream->pb.ioResult) {    /* got back error status? */
  308.     sprintf (tmp,"Can't connect to %.80s,%ld",hst.cname,port);
  309.     mm_log (tmp,ERROR);
  310.                 /* nuke the buffer */
  311.     stream->pb.csCode = TCPRelease;
  312.     createpb->userDataPtr = NIL;
  313.     if (PBControlSync (&stream->pb)) fatal ("TCPRelease lossage");
  314.                 /* free its buffer */
  315.     fs_give ((void **) &createpb->rcvBuff);
  316.     fs_give ((void **) &stream);/* and the local stream */
  317.     return NIL;
  318.   }
  319.  
  320.                 /* copy host names for later use */
  321.   stream->host = cpystr (hst.cname);
  322.                 /* tie off trailing dot */
  323.   stream->host[strlen (stream->host) - 1] = '\0';
  324.   i = openpb->localHost >> 24;    /* the open gave us our address */
  325.   j = (openpb->localHost >> 16) & 0xff;
  326.   k = (openpb->localHost >> 8) & 0xff;
  327.   l = openpb->localHost & 0xff;
  328.   sprintf (tmp,"[%ld.%ld.%ld.%ld]",i,j,k,l);
  329.   stream->localhost = cpystr (tmp);
  330.   return stream;
  331. }
  332.  
  333.  
  334. /* Called when have return from DNS
  335.  * Accepts: host info pointer
  336.  *        user data pointer
  337.  */
  338.  
  339. pascal void tcp_dns_result (struct hostInfo *hostInfoPtr,char *userDataPtr)
  340. {
  341.   /* dummy routine */
  342. }
  343.  
  344. /* TCP/IP authenticated open
  345.  * Accepts: host name
  346.  *        service name
  347.  * Returns: TCP/IP stream if success else NIL
  348.  */
  349.  
  350. TCPSTREAM *tcp_aopen (char *host,char *service)
  351. {
  352.   return NIL;            /* no authenticated opens on Mac */
  353. }
  354.  
  355. /* TCP/IP receive line
  356.  * Accepts: TCP/IP stream
  357.  * Returns: text line string or NIL if failure
  358.  */
  359.  
  360. char *tcp_getline (TCPSTREAM *stream)
  361. {
  362.   int n,m;
  363.   char *st,*ret,*stp;
  364.   char c = '\0';
  365.   char d;
  366.                 /* make sure have data */
  367.   if (!tcp_getdata (stream)) return NIL;
  368.   st = stream->iptr;        /* save start of string */
  369.   n = 0;            /* init string count */
  370.   while (stream->ictr--) {    /* look for end of line */
  371.     d = *stream->iptr++;    /* slurp another character */
  372.     if ((c == '\015') && (d == '\012')) {
  373.       ret = (char *) fs_get (n--);
  374.       memcpy (ret,st,n);    /* copy into a free storage string */
  375.       ret[n] = '\0';        /* tie off string with null */
  376.       return ret;
  377.     }
  378.     n++;            /* count another character searched */
  379.     c = d;            /* remember previous character */
  380.   }
  381.                 /* copy partial string from buffer */
  382.   memcpy ((ret = stp = (char *) fs_get (n)),st,n);
  383.                 /* get more data from the net */
  384.   if (!tcp_getdata (stream)) return NIL;
  385.                 /* special case of newline broken by buffer */
  386.   if ((c == '\015') && (*stream->iptr == '\012')) {
  387.     stream->iptr++;        /* eat the line feed */
  388.     stream->ictr--;
  389.     ret[n - 1] = '\0';        /* tie off string with null */
  390.   }
  391.                 /* else recurse to get remainder */
  392.   else if (st = tcp_getline (stream)) {
  393.     ret = (char *) fs_get (n + 1 + (m = strlen (st)));
  394.     memcpy (ret,stp,n);        /* copy first part */
  395.     memcpy (ret + n,st,m);    /* and second part */
  396.     fs_give ((void **) &stp);    /* flush first part */
  397.     fs_give ((void **) &st);    /* flush second part */
  398.     ret[n + m] = '\0';        /* tie off string with null */
  399.   }
  400.   return ret;
  401. }
  402.  
  403. /* TCP/IP receive buffer
  404.  * Accepts: TCP/IP stream
  405.  *        size in bytes
  406.  *        buffer to read into
  407.  * Returns: T if success, NIL otherwise
  408.  */
  409.  
  410. long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer)
  411. {
  412.   unsigned long n;
  413.   char *bufptr = buffer;
  414.   while (size > 0) {        /* until request satisfied */
  415.     if (!tcp_getdata (stream)) return NIL;
  416.     n = min (size,stream->ictr);/* number of bytes to transfer */
  417.                 /* do the copy */
  418.     memcpy (bufptr,stream->iptr,n);
  419.     bufptr += n;        /* update pointer */
  420.     stream->iptr +=n;
  421.     size -= n;            /* update # of bytes to do */
  422.     stream->ictr -=n;
  423.   }
  424.   bufptr[0] = '\0';        /* tie off string */
  425.   return T;
  426. }
  427.  
  428.  
  429. /* TCP/IP receive data
  430.  * Accepts: TCP/IP stream
  431.  * Returns: T if success, NIL otherwise
  432.  */
  433.  
  434. long tcp_getdata (TCPSTREAM *stream)
  435. {
  436.   struct TCPReceivePB *receivepb = &stream->pb.csParam.receive;
  437.   struct TCPAbortPB *abortpb = &stream->pb.csParam.abort;
  438.   while (stream->ictr < 1) {    /* if nothing in the buffer */
  439.     stream->pb.csCode = TCPRcv;    /* receive TCP data */
  440.                 /* wait forever */
  441.     receivepb->commandTimeoutValue = 0;
  442.     receivepb->rcvBuff = stream->ibuf;
  443.     receivepb->rcvBuffLen = BUFLEN;
  444.     receivepb->secondTimeStamp = 0;
  445.     receivepb->userDataPtr = NIL;
  446.     PBControlAsync (&stream->pb);/* now read the data */
  447.     while (stream->pb.ioResult == inProgress && wait ());
  448.     if (stream->pb.ioResult) {    /* punt if got an error */
  449.                     /* nuke connection */
  450.       stream->pb.csCode = TCPAbort;
  451.       abortpb->userDataPtr = NIL;
  452.       PBControlSync (&stream->pb);
  453.       return NIL;
  454.     }
  455.     stream->iptr = stream->ibuf;/* point at TCP buffer */
  456.     stream->ictr = receivepb->rcvBuffLen;
  457.   }
  458.   return T;
  459. }
  460.  
  461. /* TCP/IP send string as record
  462.  * Accepts: TCP/IP stream
  463.  *        string pointer
  464.  * Returns: T if success else NIL
  465.  */
  466.  
  467. long tcp_soutr (TCPSTREAM *stream,char *string)
  468. {
  469.   return tcp_sout (stream,string,(unsigned long) strlen (string));
  470. }
  471.  
  472.  
  473. /* TCP/IP send string
  474.  * Accepts: TCP/IP stream
  475.  *        string pointer
  476.  *        byte count
  477.  * Returns: T if success else NIL
  478.  */
  479.  
  480. long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
  481. {
  482.   struct TCPSendPB *sendpb = &stream->pb.csParam.send;
  483.   struct TCPAbortPB *abortpb = &stream->pb.csParam.abort;
  484.   struct {
  485.     unsigned short length;
  486.     Ptr buffer;
  487.     unsigned short trailer;
  488.   } wds;
  489.   stream->pb.csCode = TCPSend;    /* send TCP data */
  490.                 /* wait a maximum of 60 seconds */
  491.   sendpb->ulpTimeoutValue = 60;
  492.   sendpb->ulpTimeoutAction = 0;
  493.   sendpb->validityFlags = timeoutValue|timeoutAction;
  494.   sendpb->pushFlag = T;        /* send the data now */
  495.   sendpb->urgentFlag = NIL;    /* non-urgent data */
  496.   sendpb->wdsPtr = (Ptr) &wds;
  497.   sendpb->userDataPtr = NIL;
  498.   wds.length = size;        /* size of buffer */
  499.   wds.buffer = string;        /* buffer */
  500.   wds.trailer = 0;        /* tie off buffer */
  501.   PBControlAsync (&stream->pb);    /* now send the data */
  502.   while (stream->pb.ioResult == inProgress && wait ());
  503.   if (stream->pb.ioResult) {    /* punt if got an error */
  504.     stream->pb.csCode =TCPAbort;/* nuke connection */
  505.     abortpb->userDataPtr = NIL;
  506.     PBControlSync (&stream->pb);/* sayonara */
  507.     return NIL;
  508.   }
  509.   return T;            /* success */
  510. }
  511.  
  512. /* TCP/IP close
  513.  * Accepts: TCP/IP stream
  514.  */
  515.  
  516. void tcp_close (TCPSTREAM *stream)
  517. {
  518.   struct TCPClosePB *closepb = &stream->pb.csParam.close;
  519.   struct TCPCreatePB *createpb = &stream->pb.csParam.create;
  520.   stream->pb.csCode = TCPClose;    /* close TCP stream */
  521.   closepb->ulpTimeoutValue = 15;/* wait a maximum of 15 seconds */
  522.   closepb->ulpTimeoutAction = 0;
  523.   closepb->validityFlags = timeoutValue|timeoutAction;
  524.   closepb->userDataPtr = NIL;
  525.   PBControlAsync (&stream->pb);    /* now close the connection */
  526.   while (stream->pb.ioResult == inProgress && wait ());
  527.   stream->pb.csCode =TCPRelease;/* flush the buffers */
  528.   createpb->userDataPtr = NIL;
  529.   if (PBControlSync (&stream->pb)) fatal ("TCPRelease lossage");
  530.                 /* free its buffer */
  531.   fs_give ((void **) &createpb->rcvBuff);
  532.                 /* flush host names */
  533.   fs_give ((void **) &stream->host);
  534.   fs_give ((void **) &stream->localhost);
  535.   fs_give ((void **) &stream);    /* flush the stream */
  536. }
  537.  
  538. /* TCP/IP return host for this stream
  539.  * Accepts: TCP/IP stream
  540.  * Returns: host name for this stream
  541.  */
  542.  
  543. char *tcp_host (TCPSTREAM *stream)
  544. {
  545.   return stream->host;        /* return host name */
  546. }
  547.  
  548.  
  549. /* TCP/IP return local host for this stream
  550.  * Accepts: TCP/IP stream
  551.  * Returns: local host name for this stream
  552.  */
  553.  
  554. char *tcp_localhost (TCPSTREAM *stream)
  555. {
  556.   return stream->localhost;    /* return local host name */
  557. }
  558.  
  559. /* These functions are only used by rfc822.c for calculating cookies.  So this
  560.  * is good enough.  If anything better is needed fancier functions will be
  561.  * needed.
  562.  */
  563.  
  564.  
  565. /* Return host ID
  566.  */
  567.  
  568. unsigned long gethostid ()
  569. {
  570.   return 0xdeadface;
  571. }
  572.  
  573.  
  574. /* Return random number
  575.  */
  576.  
  577. long random ()
  578. {
  579.   return (long) rand () << 16 + rand ();
  580. }
  581.  
  582.  
  583. /* Return `process ID'
  584.  */
  585.  
  586. long getpid ()
  587. {
  588.   return 1;
  589. }
  590.  
  591. /* Block until event satisfied
  592.  * Called as: while (wait_condition && wait ());
  593.  * Returns T if OK, NIL if user wants to abort
  594.  *
  595.  * Allows user to run a desk accessory, select a different window, or go
  596.  * to another application while waiting for the event to finish.  COMMAND/.
  597.  * will abort the wait.
  598.  * Assumes the Apple menu has the apple character as its first character,
  599.  * and that the main program has disabled all other menus.
  600.  */
  601.  
  602. long wait ()
  603. {
  604.   EventRecord event;
  605.   WindowPtr window;
  606.   MenuInfo **m;
  607.   long r;
  608.   Str255 tmp;
  609.                 /* wait for an event */
  610.   WaitNextEvent (everyEvent,&event,(long) 6,NIL);
  611.   switch (event.what) {        /* got one -- what is it? */
  612.   case mouseDown:        /* mouse clicked */
  613.     switch (FindWindow (event.where,&window)) {
  614.     case inMenuBar:        /* menu bar item? */
  615.                 /* yes, interesting event? */    
  616.       if (r = MenuSelect (event.where)) {
  617.                 /* round-about test for Apple menu */
  618.       if ((*(m = GetMHandle (HiWord (r))))->menuData[1] == appleMark) {
  619.                 /* get desk accessory name */ 
  620.       GetItem (m,LoWord (r),&tmp);
  621.       OpenDeskAcc (tmp);    /* fire it up */
  622.       SetPort (window);    /* put us back at our window */
  623.     }
  624.     else SysBeep (60);    /* the fool forgot to disable it! */
  625.       }
  626.       HiliteMenu (0);        /* unhighlight it */
  627.       break;
  628.     case inContent:        /* some window was selected */
  629.       if (window != FrontWindow ()) SelectWindow (window);
  630.       break;
  631.     default:            /* ignore all others */
  632.       break;
  633.     }
  634.     break;
  635.   case keyDown:            /* key hit - if COMMAND/. then punt */
  636.     if ((event.modifiers & cmdKey) && (event.message & charCodeMask) == '.')
  637.       return NIL;
  638.     break;
  639.   default:            /* ignore all others */
  640.     break;
  641.   }
  642.   return T;            /* try wait test again */
  643. }
  644.  
  645. /* Subscribe to mailbox
  646.  * Accepts: mailbox name
  647.  * Returns: T on success, NIL on failure
  648.  */
  649.  
  650. long sm_subscribe (char *mailbox)
  651. {
  652.   return NIL;            /* needs to be written */
  653. }
  654.  
  655.  
  656. /* Unsubscribe from mailbox
  657.  * Accepts: mailbox name
  658.  * Returns: T on success, NIL on failure
  659.  */
  660.  
  661. long sm_unsubscribe (char *mailbox)
  662. {
  663.   return NIL;            /* needs to be written */
  664. }
  665.  
  666.  
  667. /* Read subscription database
  668.  * Accepts: pointer to subscription database handle (handle NIL if first time)
  669.  * Returns: character string for subscription database or NIL if done
  670.  */
  671.  
  672. char *sm_read (void **sdb)
  673. {
  674.   return NIL;
  675. }
  676.